// ignore_for_file: depend_on_referenced_packages, implementation_imports
/*
 * @Author: zi_qi
 * @Date: 2024-07-19 14:05:52
 * @LastEditors: ziqi jhzq12345678
 * @LastEditTime: 2025-01-21 19:58:07
 * @Description: 
 */

import 'dart:async';
import 'dart:convert';
import 'package:file/file.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_cache_manager/src/cache_store.dart';
import 'package:flutter_cache_manager/src/web/web_helper.dart';
import 'package:uuid/uuid.dart';

const String keyyyy = '2020-zq3-888';

class CustomCacheManager implements BaseCacheManager {
  static CacheManagerLogLevel logLevel = CacheManagerLogLevel.none;

  CustomCacheManager(Config config)
      : _config = config,
        _store = CacheStore(config) {
    _webHelper = WebHelper(_store, config.fileService);
  }

  final Config _config;

  Config get config => _config;

  final CacheStore _store;

  CacheStore get store => _store;

  late final WebHelper _webHelper;

  WebHelper get webHelper => _webHelper;

  @override
  Future<File> getSingleFile(
    String url, {
    String? key,
    Map<String, String>? headers,
  }) async {
    key ??= url;
    final cacheFile = await getFileFromCache(key);
    if (cacheFile != null && cacheFile.validTill.isAfter(DateTime.now())) {
      return cacheFile.file;
    }
    return (await downloadFile(url, key: key, authHeaders: headers)).file;
  }

  @override
  @Deprecated('Prefer to use the new getFileStream method')
  Stream<FileInfo> getFile(String url,
      {String? key, Map<String, String>? headers}) {
    return getFileStream(
      url,
      key: key,
      withProgress: false,
    ).where((r) => r is FileInfo).cast<FileInfo>();
  }

  @override
  Stream<FileResponse> getFileStream(String url,
      {String? key, Map<String, String>? headers, bool withProgress = false}) {
    key ??= url;
    final streamController = StreamController<FileResponse>();
    _pushFileToStream(streamController, url, key, headers, withProgress);
    return streamController.stream;
  }

  Future<void> _pushFileToStream(
    StreamController<dynamic> streamController,
    String url,
    String? key,
    Map<String, String>? headers,
    bool withProgress,
  ) async {
    key ??= url;
    FileInfo? cacheFile;
    try {
      cacheFile = await getFileFromCache(key);
      if (cacheFile != null) {
        streamController.add(cacheFile);
        withProgress = false;
      }
    } on Object catch (e) {
      cacheLogger.log(
          'CacheManager: Failed to load cached file for $url with error:\n$e',
          CacheManagerLogLevel.debug);
    }
    if (cacheFile == null || cacheFile.validTill.isBefore(DateTime.now())) {
      try {
        await for (final response in _webHelper
            .downloadFile(url, key: key, authHeaders: headers)
            .timeout(
          const Duration(seconds: 20),
          onTimeout: (sink) {
            streamController.addError('');
          },
        )) {
          if (response is DownloadProgress && withProgress) {
            streamController.add(response);
          }
          if (response is FileInfo) {
            // await compute(_decodeImgData, response);
            await _decodeImgData(response);
            streamController.add(response);
          }
        }
      } on Object catch (e) {
        cacheLogger.log(
            'CacheManager: Failed to download file from $url with error:\n$e',
            CacheManagerLogLevel.debug);
        if (cacheFile == null && streamController.hasListener) {
          streamController.addError(e);
        }

        if (cacheFile != null &&
            e is HttpExceptionWithStatus &&
            e.statusCode == 404) {
          if (streamController.hasListener) {
            streamController.addError(e);
          }
          await removeFile(key);
        }
      }
    }
    streamController.close();
  }

  ////图片解密
  Future _decodeImgData(FileInfo response) async {
    FileInfo f = response;
    Uint8List fileBytes = await f.file.readAsBytes();
    final kkkkkk = utf8.encode(keyyyy);
    Uint8List bytes = fileBytes;
    for (int i = 0; i < 100; i++) {
      bytes[i] ^= kkkkkk[i % kkkkkk.length];
    }
    await f.file.writeAsBytes(bytes);
  }

  ///Download the file and add to cache
  @override
  Future<FileInfo> downloadFile(String url,
      {String? key,
      Map<String, String>? authHeaders,
      bool force = false}) async {
    key ??= url;
    final fileResponse = await _webHelper
        .downloadFile(
          url,
          key: key,
          authHeaders: authHeaders,
          ignoreMemCache: force,
        )
        .firstWhere((r) => r is FileInfo);
    return fileResponse as FileInfo;
  }

  @override
  Future<FileInfo?> getFileFromCache(String key,
          {bool ignoreMemCache = false}) =>
      _store.getFile(key, ignoreMemCache: ignoreMemCache);

  @override
  Future<FileInfo?> getFileFromMemory(String key) =>
      _store.getFileFromMemory(key);

  @override
  Future<File> putFile(
    String url,
    Uint8List fileBytes, {
    String? key,
    String? eTag,
    Duration maxAge = const Duration(days: 30),
    String fileExtension = 'file',
  }) async {
    key ??= url;
    var cacheObject = await _store.retrieveCacheData(key);
    cacheObject ??= CacheObject(
      url,
      key: key,
      relativePath: '${const Uuid().v1()}.$fileExtension',
      validTill: DateTime.now().add(maxAge),
    );

    cacheObject = cacheObject.copyWith(
      validTill: DateTime.now().add(maxAge),
      eTag: eTag,
    );

    final file = await _config.fileSystem.createFile(cacheObject.relativePath);
    await file.writeAsBytes(fileBytes);
    _store.putFile(cacheObject);
    return file;
  }

  @override
  Future<File> putFileStream(
    String url,
    Stream<List<int>> source, {
    String? key,
    String? eTag,
    Duration maxAge = const Duration(days: 30),
    String fileExtension = 'file',
  }) async {
    key ??= url;
    var cacheObject = await _store.retrieveCacheData(key);
    cacheObject ??= CacheObject(url,
        key: key,
        relativePath: '${const Uuid().v1()}'
            '.$fileExtension',
        validTill: DateTime.now().add(maxAge));

    cacheObject = cacheObject.copyWith(
      validTill: DateTime.now().add(maxAge),
      eTag: eTag,
    );

    final file = await _config.fileSystem.createFile(cacheObject.relativePath);

    final sink = file.openWrite();
    await source.map((event) => event).pipe(sink);

    _store.putFile(cacheObject);
    return file;
  }

  @override
  Future<void> removeFile(String key) async {
    final cacheObject = await _store.retrieveCacheData(key);
    if (cacheObject?.id != null) {
      await _store.removeCachedFile(cacheObject!);
    }
  }

  @override
  Future<void> emptyCache() => _store.emptyCache();

  @override
  Future<void> dispose() async {
    await _config.repo.close();
  }
}

class DefaultCustomCacheManager extends CustomCacheManager
    with ImageCacheManager {
  DefaultCustomCacheManager(super.config);
}
